/*
 * Copyright: Public Domain
 */
module nonstd.typecons;

public import std.typecons;
import std.metastrings;
import std.typetuple;

import nonstd.traits;


/**
Generates implementations for the specified functions.

Params:
  Generator  = See the example below.
  FuncTraits = A tuple of $(D FunctionTraits) on the functions to be
               implemented.

Example:
--------------------
interface Base
{
    int foo(int n);
    double bar(double n);
}
template MyGenerator()
{
    // generateFunction is a template or template function, which
    // returns the function body for the given FunctionTrait ft.
    template generateFunction(alias ft)
    {
        enum string generateFunction =
            "return _p0;" ; // parameters are _p0, _p1, ...
    }
}
class MyClass : Base
{
    // Base.foo and Base.bar are implemented.
    mixin generateFunctions!(MyGenerator!(),
        TypeTuple!(FunctionTraits!(Base.foo),
                   FunctionTraits!(Base.bar)));
}
void main()
{
    auto c = new MyClass;
    assert(c.foo(42) == 42);
    assert(c.bar(3.14) == 3.14);
}
--------------------

Bugs:
  $(BUGZILLA 2740) should be fixed.
 */
template generateFunctions(alias Generator, FuncTraits...)
{
    private alias FuncTraits _FT; // for traitsID
    mixin (generateFunctionsImpl!(Generator, "_FT", _FT).code);
}

// [internal]
private template generateFunctionsImpl(
        alias Generator, string traitsID, FuncTraits...)
{
    /*
     * Why is traitsID necessary?
     *
     * When this code is generated:
     * --------------------
     * module modA;
     * class AutoImpl(Base) : Base
     * {
     *     some.another.module.T foo(); // generated
     * }
     * --------------------
     * Then the compiler can't tell what the T is, and spits an error:
     * "identifier 'some.another.module.T' is not defined".
     *
     * traitsID is a string that evaluates to a function traits tuple
     * when mixed in the scope where a genearted code is mixed. To
     * prevent not-defined error, we use traitsID for indirectly
     * referencing the return types and parameter types in the
     * generated code.
     */

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //

    /*
     * [workaround] FuncTraits[i].sth (intermediate indexing) does not
     * compile. We have to declare __FuncTraits_0, __FuncTraits_1, etc.
     * and use them instead.
     */
    template unrollTraits(size_t i)
    {
        static if (i < FuncTraits.length)
            enum unrollTraits =
                "alias " ~ traitsID ~ "[" ~ ToString!(i) ~ "] "
                     "_" ~ traitsID ~ "_" ~ ToString!(i) ~ ";\n" ~
                unrollTraits!(i + 1);
        else
            enum unrollTraits = "";
    }
    enum preamble =
        "private\n"
        "{\n"
            ~ unrollTraits!(0) ~
        "}\n";

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //

    /*
     * Generate the i-th function and returns the code in .code.
     */
    template generateFunction(size_t i)
    {
        alias FuncTraits[i] ft;
        enum FTi = "_" ~ traitsID ~ "_" ~ ToString!(i);

        /*
         * Various modifiers
         */
        enum link   = "extern(" ~ linkageToString(ft.linkage) ~ ")";
        enum stoc   = storageClassToString(ft.storageClass);
        enum attrs  = funcAttrToString(ft.attributes);
        enum prefix = link ~ " " ~ stoc ~ " " ~ attrs;

        /*
         * Return type
         */
        enum rtype = FTi ~ ".ReturnType";

        /*
         * Generate parameters
         */
        string param(size_t k)()
        {
            enum stoc = storageClassToString(ft.parameterStorageClasses[k]);
            enum type = FTi ~ ".ParameterTypeTuple[" ~ ToString!(k) ~ "]";
            return stoc ~ " " ~ type ~ " _p" ~ ToString!(k);
        }

        string params()
        {
            string params = "";

            foreach (k, P; ft.ParameterTypeTuple)
            {
                static if (k >= 1)
                    params ~= ", ";
                params ~= param!(k);
            }

            // classic variadic arguments: ( foo, ... ) or ( ... )
            static if (ft.variadic == Variadic.VARIADIC)
                params ~= ft.ParameterTypeTuple.length ?
                    ", ..." : "...";

            // typesafe variadic arguments: ( foo ... )
            static if (ft.variadic == Variadic.TYPESAFE)
                params ~= "...";

            return params;
         }

        /*
         * Finish
         */
        enum code =
            prefix ~ " " ~ rtype ~ " " ~ ft.name ~ "(" ~ params ~ ")\n"
            "{\n"
                ~ Generator.generateFunction!(ft) ~ "\n"
            "}";
    }

    // Generate i, i+1, ...-th methods.
    template foreachFunction(size_t i)
    {
        static if (i < FuncTraits.length)
            enum foreachFunction =
                generateFunction!(i).code ~ "\n" ~
                foreachFunction!(i + 1);
        else
            enum foreachFunction = "";
    }

    enum impls = foreachFunction!(0);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //

    /*
     * Finish
     */
    enum code = preamble ~ "\n" ~ impls;

    debug (std_typecons_printGeneratedCode)
    {
        pragma(msg, "--------------------");
        pragma(msg, code);
        pragma(msg, "--------------------");
    }
}


// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //
// BlackHole and WhiteHole
// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //

/*
Generates aliases for all member functions defined in $(D Base):
--------------------
alias Base.foo foo;
alias Base.bar bar;
...
--------------------
This template will be used for revealing hidden functions.
 */
private template generateOverloadAliases(string base, Base)
{
    enum generateOverloadAliases =
        generateOverloadAliasesImpl!(base, methodNames!(Base)).code;
}

// [internal]
private template generateOverloadAliasesImpl(string base, alias ms)
{
    static if (ms.length)
        enum code =
            "alias " ~ base ~ "." ~ ms[0] ~ " " ~ ms[0] ~ ";\n" ~
            generateOverloadAliasesImpl!(base, ms[1 .. $]).code;
    else
        enum code = "";
}


/*
Returns a tuple of $(D FunctionTraits), each of which is the traits on
a function that can override an abstract member function overloads in
$(D C) or its ancestors.
 */
private template AbstractOverrideTraitsTuple(C)
{
    alias OverrideTraitsTuple!(
            CovariantOverloadSets!(AbstractMethodsTuple!(C))
        ) AbstractOverrideTraitsTuple;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //

/**
$(D BlackHole!(Base)) is a class which implements all abstract methods
defined in $(D Base) and its ancestors. Each method just returns the
$(D .init) property of its return type.

Example:
--------------------
interface I { int foo(); }
void main()
{
    auto bh = new BlackHole!I;
    assert(bh.foo == 0);
}
--------------------
 */
class BlackHole(Base) : Base
{
    /*
     * Implement abstract methods.
     */
    mixin generateFunctions!(BlackHoleGenerator!(Base),
        AbstractOverrideTraitsTuple!(Base));

    /*
     * The generated methods would hide ones defined in Base. This mixin
     * reveals hidden methods.
     */
    mixin (generateOverloadAliases!("Base", Base));
}

private template BlackHoleGenerator(Base)
{
    // The generated function does nothing and returns .init.
    string generateFunction(alias mt)()
    {
        string stmt;

        static if (is(mt.ReturnType == void))
            stmt = "";
        else static if (mt.attributes & FuncAttr.REF)
            // reference to a dummy variable
            stmt = "static __gshared typeof(return) dummy;\n"
                   "return dummy;";
        else
            stmt = "return typeof(return).init;";
        return stmt;
    }
}


unittest
{
    struct Scope
    {
        interface X {}
        interface Y : X {}
        interface I { X foo() nothrow; }
        interface J { Y foo() const; }
        class C : I, J
        {
            alias I.foo foo;
            alias J.foo foo;
            abstract ref int foo(lazy short i, int[] a...);
            extern (C) abstract void bar(size_t n, ...);
            final int bar() const pure nothrow { return 42; }
        }
    }
    auto bh = new BlackHole!(Scope.C);

    Scope.Y y = (cast(immutable) bh).foo();
    assert(y is null);

    int* p = &bh.foo((assert(0), 1), 2, 3, 4); // lazy eval
    assert(p != null); // a static variable is returned

    bh.bar(4, 3, 2, 1); // C-style variadic function
    assert(bh.bar() == 42); // final method is not overridden
}


/**
$(D WhiteHole!(Base)) is a class which implements all abstract methods
defined in $(D Base) and its ancestors. Each method will throw an
$(D AssertError) when it's invoked.

Example:
--------------------
interface I { real foo(); }
void main()
{
    auto wh = new WhiteHole!I;
    wh.foo(); // throws AssertError
}
--------------------
 */
class WhiteHole(Base) : Base
{
    /*
     * Implement abstract methods.
     */
    mixin generateFunctions!(WhiteHoleGenerator!(Base),
        AbstractOverrideTraitsTuple!(Base));

    /*
     * The generated methods would hide methods defined in Base. So
     * aliases should be inserted.
     */
    mixin (generateOverloadAliases!("Base", Base));
}

private template WhiteHoleGenerator(Base)
{
    // The generated function throws AssertError.
    string generateFunction(alias mt)()
    {
        enum qname = Base.stringof ~ "." ~ mt.name;
        enum id    = qname ~ mt.ParameterTypeTuple.stringof;
        enum msg   = id ~ " is not implemented";

        /*
         * We use assert in case the method is a nothrow function
         * (assert doesn't violate nothrow).
         */
        return "enum emsg = q\"EOS_WhiteHole_\n"
                ~ msg ~
                "\nEOS_WhiteHole_\";\n"
               "assert(0, emsg[0 .. $ - 1]);";
    }
}

unittest
{
    static class B
    {
        abstract void foo();
    }
    auto wh = new WhiteHole!B;
    try { wh.foo(); assert(0); } catch (Error e) { }
}

